import { LazyList, LazyNumber, LazyPair } from "./types"


export const power =
    (a: LazyNumber) =>
        (b: LazyNumber)
            : LazyNumber =>
            () => Math.pow(a(), b())

export const multiply =
    (a: LazyNumber) =>
        (b: LazyNumber)
            : LazyNumber =>
            () => a() * b()


export const square = (a: LazyNumber): LazyNumber =>
    () => (a() * a());

export const round = (x: LazyNumber): LazyNumber =>
    () => Math.round(x())

export const log2 = (x: LazyNumber): LazyNumber =>
    () => Math.log2(x())

export function range(first: LazyNumber): LazyList<number> {
    return () => {
        const x = first();
        return {
            head: () => x,
            tail: range(() => (x + 1))
        };
    }
}

export const within =
    (eps: LazyNumber) =>
        (xs: LazyList<number>): LazyNumber => {
            let pair = xs();
            if (pair == null) {
                console.log("[warn] got in impossible path");
                return () => 0;
            }
            let x = pair.head();
            let t = pair.tail()
            if (t == null) {
                return () => x;
            }
            let y = t.head()
            if (Math.abs(x - y) <= eps()) {
                return () => y;
            }
            return within(eps)(pair.tail)
        }


export const relative =
    (eps: LazyNumber) =>
        (xs: LazyList<number>): LazyNumber => {
            const pair = xs()
            if (pair === null) {
                console.log("[warn] got in impossible path");
                return () => 0;
            }
            const a = pair.head();
            const t = pair.tail();
            if (t === null) {
                return () => a;
            }
            const b = t.head()
            if (Math.abs(a - b) <= eps() * Math.abs(b)) {
                return () => b;
            }
            return relative(eps)(() => t)
        }



export const addPair = (lp: LazyPair<number, number>): LazyNumber => {
    const p = lp();
    if (p === null) {
        console.log("invalid input for func `order`");
        return () => 0;
    }
    const a = p.a();
    const b = p.b();
    return () => (a + b);
}
